/**
 *
 * 自定义VueRouter插件
 * Vue 注入插件的时候 分为两种情况
 *     一、插件是一个函数 Vue的实例 vue .use（） 直接调用插件的函数
 *     二、插件是一个对象（class对象等）Vue的实例 vue .use（） 要直接调用插件对象的install方法
 *
 * vueRouter插件 是一个class对象 所以 vueRouter对象一定会有一个install方法
 *
 **/
let _Vue = null

export default class VueRouter {

    static install(Vue) {
        // 1. 判断当前插件是否已经安装
        if(VueRouter.install.installed) return
        VueRouter.install.installed = true
        // 2. 把Vue构造函数记录到全局变量
        _Vue = Vue
        // 3. 把创建Vue实例时传入的router对象注入到vue实例上
        // 混入
        _Vue.mixin({
            // 给所有的vue实例设置一个选项  所有的组件都会使用这个混入的beforeCreate这个组件
            beforeCreate() {
                // 如果是组件的话就不执行 如果是实例才执行
                if(this.$options.router) {  // 只有vue实例的options中才会有router 组件中没有
                    _Vue.prototype.$router = this.$options.router
                    this.$options.router.init()
                }
            }
        })
    }

    constructor(options) {
        this.options = options  //记录构造函数中传入的options
        this.routeMap = {}
        //根据options中的routes（路由规则）解析出来 存储到routeMap里面来
        //routeMap存储的是键值对 键是路由地址 值是对应的组件  根据路由地址在routeMap里找到对应的组件然后渲染出来
        this.data = _Vue.observable({
            // 实现将当前这个对象变成为响应式对象
            current: '/'
        })
        //data是响应式对象 这里面有一个current属性 里面是对应的当前的路由地址
    }

    init() {
        this.createRouteMap()
        this.initComponents(_Vue)
        this.initEvent()
    }

    createRouteMap() {
        // 遍历所有的路由规则 然后将所有的路由规则解析成简直对的形式 存储到routeMap
        // options中的routes数组中存储了所有的路由规则
        this.options.routes.forEach(route => {
            this.routeMap[route.path] = route.component
        })
    }

    initComponents(Vue) {
        Vue.component('router-link', {
            props: {
                to: String
            },
            // 运行时版本的vue 不支持template 在完整版的vue中使用该template会好用
            // template: '<a :href="to"><slot></slot></a>'
            render(h) {
                // h函数的作用是创建虚拟DOM  render函数调用h函数 并将结果返回 h函数是vue反给我们的
                // h 函数有三个参数
                //   1 我们需要创建的这个元素对应的选择器
                //   2 我们需要给创建的这个元素设置的属性
                //   3 设置生成的这个元素的子元素
                return h(
                    'a',
                    {
                        attrs: { // DOM 对象的属性 放在attrs中来写
                            href: this.to
                        },
                        on: { // 给DOM对象注册事件
                            click: this.clickHandler  // 给click注册一个点击事件
                        }
                    },
                    [this.$slots.default]) // 自元素 默认版本的插槽
            },
            methods: {
                clickHandler(e) {
                    history.pushState({}, '', this.to)
                    this.$router.data.current = this.to

                    e.preventDefault()
                }
            }
        })

        const self = this
        Vue.component('router-view', {
            render(h) {
                // 找到当前的路由地址 并通过当前的路由地址找到对应的路由组件
                // 然后再调用h函数 将找到的路由组件转化为虚拟DOM 然后再返回
                // （h函数可以直接将一个组件转化为一个虚拟DOM）
                // 当前路由地址
                // self.data.current
                const component = self.routeMap[self.data.current]

                return h(component)
            }
        })
    }

    // 注册popState事件
    initEvent() {
        window.addEventListener('popstate', () => {
            this.data.current = window.location.pathname
        })
    }
}